<?php
namespace Matrix;

use Matrix\Routing\Router;

/**
 * 包管理器
 *
 * 通过包管理器可以更加灵活的将一个应用拆分N个部分, 方便重用, 降低耦合
 * 包可以包含路由, 控制器, 模块, 视图等, 处理配置
 *
 * 注册一个包:
 * <code>
 * Bundle::register('admin', array('handles' => 'admin'));
 * </code>
 *
 * 检测包是否已经注册:
 * <code>
 * Bundle::registered('admin');
 * </code>
 *
 * 包可以自动启动, 也可以手动启动
 * 自动启动包:
 * <code>
 * Bundle::register('admin', array('auto' => true));
 * </code>
 * 手动启动包:
 * <code>
 * Bundle::start('admin);
 * </code>
 *
 * 禁用包:
 * <code>
 * Bundle::stop('admin');
 * </code>
 */
class Bundle
{
	/**
	 * 所有安装的包
	 *
	 * @var array
	 */
	public static $bundles = array();
	
	/**
	 * 解析包元素的缓存
	 *
	 * @var array
	 */
	public static $elements = array();
	
	/**
	 * 已经启动的包
	 *
	 * @var array
	 */
	public static $started = array();
	
	/**
	 * 已经加载路由文件的包
	 *
	 * @var array
	 */
	public static $routed = array();
	
	/**
	 * 判定一个包是否已经被注册
	 *
	 * @param	string	$bundle
	 * @return	boolean
	 */
	public static function registered($bundle)
	{
		return $bundle === DEFAULT_BUNDLE || isset(static::$bundles[$bundle]);
	}
	
	/**
	 * 注册一个包
	 * 
	 * @param	string	$bundle
	 * @param	array	$config
	 * @return	void
	 */
	public static function register($bundle, $config = array())
	{
		$defaults = array('handles' => null, 'auto' => false);
		if (is_string($config)) {
			$bundle = $config;
			$config = array('location' => $bundle);
		}
		if (!isset($config['location'])) {
			$config['location'] = $bundle;
		}
		static::$bundles[$bundle] = array_merge($defaults, $config);
		// 如果包包含自动导入, 导入需要导入的类
		if (isset($config['autoloads'])) {
			static::autoloads($bundle, $config['autoloads']);
		}
	}
	
	/**
	 * 检测包是否已经启动
	 *
	 * @param	string	$bundle
	 * @return	boolean
	 */
	public static function started($bundle)
	{
		return in_array($bundle, static::$started);
	}
	
	/**
	 * 启动包并且运行包的启动脚本, 如果包已经启动, 什么都不做
	 *
	 * @param	string	$bundle
	 * @return	void
	 */
	public static function start($bundle)
	{
		if (static::started($bundle)) {
			return;
		}
		if (!static::registered($bundle)) {
			throw new \Exception("Bundle [$bundle] has not been installed.");
		}
		if (($starter = static::option($bundle, 'starter')) !== null) {
			$starter();
		} else if (file_exists($path = static::path($bundle).'start'.EXT)) {
			include $path;
		}
		// 如果包存在路由文件, 导入包的路由文件
		static::routes($bundle);
		// 触发matrix.started: bundleName事件
		Event::fire("matrix.started: {$bundle}");
		static::$started[] = $bundle;
	}
	
	/**
	 * 检测包路由文件是否已经载入
	 *
	 * @param	string	$bundle
	 * @return	boolean
	 */
	public static function routed($bundle)
	{
		return in_array($bundle, static::$routed);
	}
	
	/**
	 * 如果包存在路由文件, 导入路由文件
	 * 如果路由文件已经被导入, 什么都不做
	 *
	 * @param	string	$bundle
	 * @return	void
	 */
	public static function routes($bundle)
	{
		if (static::routed($bundle)) {
			return;
		}
		$path = static::path($bundle).'routes'.EXT;
		// 设置占位符(:bundle)被替换的值
		Router::$bundle = static::option($bundle, 'handles');
		if (file_exists($path)) {
			static::$routed[] = $bundle;
			include $path;
		}
	}
	
	/**
	 * 禁用一个包
	 *
	 * @param	string	$bundle
	 * @return	void
	 */
	public static function stop($bundle)
	{
		unset(static::$bundles[$bundle]);
	}
	
	/**
	 * 如果包名存在返回包名, 否则返回默认包名
	 *
	 * @param	string	$bundle
	 * @return	string
	 */
	public static function resolve($bundle)
	{
		if (static::registered($bundle)) {
			return $bundle;
		}
		return DEFAULT_BUNDLE;
	}
	
	/**
	 * 为包注册自动导入
	 *
	 * @param	string	$bundle
	 * @param	array	$config
	 * @return	void
	 */
	public static function autoloads($bundle, $config)
	{
		$path = rtrim(Bundle::path($bundle), DS);
		foreach ($config as $type => $mappings) {
			// 替换占位符(:bundle)为相对应的路径
			$mappings = array_map(function($mapping) use ($path) {
				return str_replace('(:bundle)', $path, $mapping);
			}, $mappings);
			Autoloader::$type($mappings);
		}
	}
	
	/**
	 * 确定指定URI用的是那个包
	 *
	 * @param	string	$uri
	 * @return	string
	 */
	public static function handles($uri)
	{
		$uri = str_finish($uri, '/');
		foreach (static::$bundles as $key => $value) {
			if (isset($value['handles']) && starts_with($uri, $value['handles'].'/')) {
				return $key;
			}
		}
		return DEFAULT_BUNDLE;
	}
	
	/**
	 * 获取包的配置信息
	 *
	 * @param	string	$bundle
	 * @return	mixed
	 */
	public static function get($bundle)
	{
		return array_get(static::$bundles, $bundle);
	}
	
	/**
	 * 获取指定包的配置选项
	 *
	 * @param	string	$bundle
	 * @param	string	$option
	 * @param	mixed	$default
	 * @return	mixed
	 */
	public static function option($bundle, $option, $default = null)
	{
		$bundle = static::get($bundle);
		if ($bundle === null) {
			return value($default);
		}
		return array_get($bundle, $option, $default);
	}
	
	/**
	 * 返回包路径
	 *
	 * @param	string	$bundle
	 * @return	string
	 */
	public static function path($bundle)
	{
		if ($bundle === null || $bundle === DEFAULT_BUNDLE) {
			return path('app');
		} else if ($location = static::option($bundle, 'location')) {
			// path: 后面跟的是绝对路径
			if (starts_with($location, 'path: ')) {
				return str_finish(substr($location, 6), DS);
			} else {
				return str_finish(path('bundle').$location, DS);
			}
		}
	}
	
	/**
	 * 返回包资源路径
	 *
	 * @param	string	$bundle
	 * @return	string
	 */
	public static function assets($bundle)
	{
		if ($bundle === null) {
			return static::assets(DEFAULT_BUNDLE);
		}
		return $bundle !== DEFAULT_BUNDLE ? "/bundles/{$bundle}/" : '/';
	}
	
	/**
	 * 从指定的标识符获取包名
	 *
	 * @param	string	$identifier
	 * @return	string
	 */
	public static function name($identifier)
	{
		list($bundle, $element) = static::parse($identifier);
		return $bundle;
	}
	
	/**
	 * 从指定的标识符获取元素
	 *
	 * @param	string	$identifier
	 * @return	string
	 */
	public static function element($identifier)
	{
		list($bundle, $element) = static::parse($identifier);
		return $element;
	}
	
	/**
	 * 根据指定的包名和元素生成标识符
	 *
	 * @param	string	$bundle
	 * @param	string	$element
	 * @return	string
	 */
	public static function identifier($bundle, $element)
	{
		if ($bundle === null || $bundle === DEFAULT_BUNDLE) {
			return $element;
		}
		return $bundle.'::'.$element;
	}
	
	/**
	 * 解析标识符为一个包含包名和元素的数组
	 *
	 * @param	string	$identifier
	 * @return	array
	 */
	public static function parse($identifier)
	{
		if (isset(static::$elements[$identifier])) {
			return static::$elements[$identifier];
		}
		if (strpos($identifier, '::') !== false) {
			$element = explode('::', strtolower($identifier));
		} else {
			$element = array(DEFAULT_BUNDLE, strtolower($identifier));
		}
		return static::$elements[$identifier] = $element;
	}
	
	/**
	 * 返回所有安装的包
	 *
	 * @return	array
	 */
	public static function all()
	{
		return static::$bundles;
	}
	
	/**
	 * 返回所有安装的包名
	 *
	 * @return	array
	 */
	public static function names()
	{
		return array_keys(static::$bundles);
	}
	
	/**
	 * 得到指定包的标识符前缀
	 *
	 * @param	string	$bundle
	 * @return	string
	 */
	public static function prefix($bundle)
	{
		return $bundle !== DEFAULT_BUNDLE ? "{$bundle}::" : '';
	}
	
	/**
	 * 得到指定包的类前缀
	 *
	 * @param	string	$bundle
	 * @return	string
	 */
	public static function class_prefix($bundle)
	{
		return $bundle !== DEFAULT_BUNDLE ? Str::classify($bundle).'_' : '';
	}
}